/*
	This is helper class for game applications
*/

#pragma once

namespace Engine
{
namespace Base
{

	//
	// Game Application
	//

	class GameApplication : public Application
	{
	// types
	private:
		SHARED_POINTER( GameApplication );


	// variables
	private:
		DEBUG_ONLY(
			TimeProfiler<double>	__fpsTimer;
			uint					__frameCounter;
		)
		
		bool					__entered	: 1;
		bool					__inited	: 1;


	// methods
	private:
		void _UpdateFPS (float dt)
		{
			++__frameCounter;

			DEBUG_ONLY(
				if ( __fpsTimer.GetTimeDelta().GetSec() > 5.0 )
				{
					float fps = float( __frameCounter / __fpsTimer.GetTimeDelta().GetSec() );
					DEBUG_CONSOLE( (string("FPS: ") << string().FormatF( fps, StringFormatF().Fmt(0,1) )).cstr() );

					__frameCounter = 0;
					__fpsTimer.Start();
				}
			)
		}


	protected:
		GameApplication (IPlatform *platform, EDevice::type deviceType) :
			Application( platform, deviceType ),
			__entered(false), __inited(false)
		{
			ESS()->GetEventSystem()->AttachEventListener(
				DelegateBuilder::Create( GameApplicationPtr(this), &GameApplication::_OnAppEvent ),		ESystemEvent::APPLICATION );

			ESS()->GetEventSystem()->AttachEventListener(
				DelegateBuilder::Create( GameApplicationPtr(this), &GameApplication::_OnUpdate ),		ESystemEvent::UPDATE );

			ESS()->GetEventSystem()->AttachEventListener(
				DelegateBuilder::Create( GameApplicationPtr(this), &GameApplication::_OnWindowEvent ),	ESystemEvent::WINDOW );

			DEBUG_ONLY(
				__frameCounter = 0;
				__fpsTimer.Start();
			)
		}


		~GameApplication ()
		{
			ESS()->GetEventSystem()->DetachEventListener(
				DelegateBuilder::Create( GameApplicationPtr(this), &GameApplication::_OnWindowEvent ),	ESystemEvent::WINDOW );
			
			ESS()->GetEventSystem()->DetachEventListener(
				DelegateBuilder::Create( GameApplicationPtr(this), &GameApplication::_OnUpdate ),		ESystemEvent::UPDATE );

			ESS()->GetEventSystem()->DetachEventListener(
				DelegateBuilder::Create( GameApplicationPtr(this), &GameApplication::_OnAppEvent ),		ESystemEvent::APPLICATION );
		}
	

		virtual void _Update (float dt) = 0;

		virtual void _OnInit () = 0;

		virtual void _OnEnter () = 0;

		virtual void _OnExit () = 0;

		virtual void _OnResize () = 0;

		virtual void _OnVisibilityChanged (bool visible) {}


	private:
		void _OnUpdate (const SysEvent &ev)
		{
			if ( ev.update.renderAvailable )
			{
				ESS()->GetInput()->PreUpdate();
				_Update( ev.update.timeDelta );
				ESS()->GetInput()->PostUpdate();
			}
			else
			{
				Thread::Delay( 10 );
			}
		}
	

		void _OnAppEvent (const SysEvent &ev)
		{
#			define WARN( _text )			/*WARNING( _text );*/ DEBUG_CONSOLE( _text )

			switch ( ev.app.type )
			{
				case SysEvent::Application::CREATED :
					WARN( "Application Created" );
					break;
				
				case SysEvent::Application::DESTROY :
					WARN( "Application Destroy" );
					MOBILE_ONLY(
						_OnExit()
					);
					break;
				
				case SysEvent::Application::ENTER_BACKGROUND :
					WARN( (string("Application Enter Background ") << (ev.app.completed ? "completed" : "")).cstr() );
					break;
				
				case SysEvent::Application::ENTER_FOREGROUND :
					WARN( (string("Application Enter Foreground ") << (ev.app.completed ? "completed" : "")).cstr() );
					if ( ev.app.completed and not __inited ) {
						_OnInit();
						__inited = true;
					}
					break;
				
				case SysEvent::Application::SURFACE_CREATED :
					WARN( "Application Surface Created" );
					if ( not __entered ) {
						_OnEnter();
						__entered = true;
					}
					break;
				
				case SysEvent::Application::SURFACE_DESTROYED :
					WARN( "Application Surface Destroyed" );
					_OnExit();
					break;
				
				case SysEvent::Application::LOW_MEMORY :
					WARN( "Application Low Memory" );
					break;
			}

#			undef WARN
		}

		void _OnWindowEvent (const SysEvent &ev)
		{
			switch ( ev.window.type )
			{
				case SysEvent::Window::RESIZED :
					_OnResize();
					return;
					
				case SysEvent::Window::SHOWN :
					_OnVisibilityChanged( false );
					return;

				case SysEvent::Window::HIDDEN :
					_OnVisibilityChanged( false );
					return;
			}
		}
	};

}	// Base
}	// Engine